﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Security.Permissions;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.Web;
using AWS.SLAM.Logging;
using AWS.SLAM.SharePoint;

namespace AWS.SLAM.WebControls
{
	/// <summary>
	/// A SaveButton that allows for delegates assigned to ItemContext.FormContext.OnSaveHandler to throw an SPException to indicate an error
	/// has occurred on save and on error bear the same result as a failure on save without an assigned ItemContext.FormContext.OnSaveHandler delegate.
	/// </summary>
	[ToolboxData("<{0}:SaveButton runat=\"server\" />")]
	public class SaveButton : Microsoft.SharePoint.WebControls.SaveButton
	{
		protected override string DefaultTemplateName
		{
			get
			{
				return "SLAMSaveButton";
			}
		}

		public SaveButton()
		{
			this.ControlMode = SPControlMode.Edit;
		}

		/*
		 * The following are decompilations of methods in Microsoft.SharePoint.WebControls.SaveButton and modified such that
		 * internal members are invoked using reflection.  The aim of replicating these methods is to modify OnBubbleEvent
		 * such that delegates assigned to OnSaveHandler can "fail" in the same way as the item saving process
		 * can fail in that delegate's absence.
		*/
		[SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
		protected override bool OnBubbleEvent(object source, EventArgs e)
		{	
			Type spFormContextType = typeof(SPFormContext);
			PropertyInfo spFormContextNextFieldName = spFormContextType.GetProperty("NextFieldName", BindingFlags.Instance | BindingFlags.NonPublic);

			string itemContextNextFieldName = Convert.ToString(spFormContextNextFieldName.GetValue(base.ItemContext.FormContext, null));

			Type spContextType = typeof(SPContext);
			PropertyInfo spContextTypeInSavePostBack = spContextType.GetProperty("InSavePostBack", BindingFlags.Instance | BindingFlags.NonPublic);
			PropertyInfo spContextContentType = spContextType.GetProperty("ContentType", BindingFlags.Instance | BindingFlags.NonPublic);
			SPContentType itemContextContentType = spContextContentType.GetValue(base.ItemContext, null) as SPContentType;

			Type spListType = typeof(SPList);
			PropertyInfo spListRootFolderUrl = spListType.GetProperty("RootFolderUrl", BindingFlags.Instance | BindingFlags.NonPublic);

			string itemContextListRootFolderUrl = Convert.ToString(spListRootFolderUrl.GetValue(base.ItemContext.List, null));

			Type spListItemType = typeof(SPListItem);
			PropertyInfo listItemTypeHasExternalDataSource = spListItemType.GetProperty("HasExternalDataSource", BindingFlags.Instance | BindingFlags.NonPublic);
			MethodInfo listItemSetExtraInfo = spListItemType.GetMethod("SetExtraInfo", BindingFlags.Instance | BindingFlags.NonPublic);
			MethodInfo listItemCheckIn = spListItemType.GetMethod("CheckIn", BindingFlags.Instance | BindingFlags.NonPublic);
			MethodInfo listItemCheckOut = spListItemType.GetMethod("CheckOut", BindingFlags.Instance | BindingFlags.NonPublic);

			bool result = false;
			if (e is CommandEventArgs)
			{
				CommandEventArgs commandEventArgs = (CommandEventArgs)e;
				if (commandEventArgs.CommandName == "SaveItem")
				{
					SPListItem listItem = base.ItemContext.ListItem;
					bool listItemHasExternalDataSource = (bool)listItemTypeHasExternalDataSource.GetValue(listItem, null);
					if (listItem != null && itemContextContentType != null && !listItemHasExternalDataSource)
					{
						try
						{
							listItem["ContentType"] = itemContextContentType.Name;
							listItemSetExtraInfo.Invoke(listItem, new object[] { "ContentTypeId", itemContextContentType.Id.ToString(), "" });
						}
						catch (ArgumentException)
						{
						}
					}
					this.Page.Validate();
					if (!this.Page.IsValid)
					{
						result = true;
					}
					else
					{
						bool flag = false;
						EventHandler onSaveHandler = base.ItemContext.FormContext.OnSaveHandler;
						if (onSaveHandler != null)
						{
							ActionBeforeSaveItem(base.ItemContext);

							try
							{
								onSaveHandler(this, EventArgs.Empty);
								flag = true;
							}
							catch (SPException ex)
							{
								flag = true;
							    if (ex.ErrorCode == 0)
							    {
							        //If OnSaveHandler delegates throw an SPException with ErrorCode 0 (signifying an "unknown" error)
							        //there should be no redirection, but instead validation messaging should
							        //appear as appropriate on the still-open form
							        flag = false;
								}
								else if (ex.ErrorCode == 1)
								{
									//If OnSaveHandler delegates throw an SPException with ErrorCode 1
									//there should be no redirection, but instead a SLAM validation messaging should
									//appear as appropriate on the still-open form
									if (!HttpContext.Current.IsCustomErrorEnabled)
									{
										flag = false;
										throw new Exception("This item successfully saved in SharePoint, but failed to be saved by SLAM.  The SLAM exception returned is: " + ex.ToString());
									}
									else
									{
										ILogger logger = ComponentService.GetInstance<ILogger>();
										logger.LogMessage("Exception", "Item failed to SLAM: " + Environment.NewLine + " List: " + List.Title + Environment.NewLine + "ListItem: " + ListItem.GetTitle() + ", " + Convert.ToString(ListItem.GetUniqueId() + Environment.NewLine + " Exception: " + ex.ToString()));
									}
								}
							}
						}
						else
						{
							if (base.List.BaseTemplate == SPListTemplateType.Survey)
							{
								if (String.IsNullOrEmpty(itemContextNextFieldName))
								{
									listItemCheckIn.Invoke(listItem, null);
									flag = true;
								}
								else
								{
									if (base.ControlMode == SPControlMode.New)
									{
										listItemCheckOut.Invoke(listItem, null);
										flag = true;
									}
									else
									{
										flag = this.SaveItem();
									}
								}
							}
							else
							{
								flag = this.SaveItem();
							}
						}
						result = true;
						if (flag)
						{
							string text = base.RedirectUrl;
							spContextTypeInSavePostBack.SetValue(base.ItemContext, true, null);
							if (this.NeedToCreateWorkSpace())
							{
								this.GotoMeetingWorkspaceCreationPage(text);
								return result;
							}
							if (!base.ItemContext.IsPopUI)
							{
								if (base.ItemContext.List.BaseType == SPBaseType.DocumentLibrary)
								{
									SPFile file = ((SPListItem)base.Item).File;
									if (file != null)
									{
										text = file.ServerRelativeUrl;
									}
									else
									{
										SPFolder folder = ((SPListItem)base.Item).Folder;
										if (folder != null)
										{
											text = folder.ServerRelativeUrl;
										}
										else
										{
											text = itemContextListRootFolderUrl;
										}
									}
								}
								SPUtility.Redirect(text, SPRedirectFlags.UseSource, this.Context);
							}
							else
							{
								if (listItem != null && listItem.File != null)
								{
									string serverRelativeUrl = listItem.File.ServerRelativeUrl;
									this.Context.Response.Write(string.Format(CultureInfo.InvariantCulture, "<script type='text/javascript'>\r\n                                    retVal = {{}};\r\n                                    retVal['newFileUrl'] = \"{0}\";\r\n                                    window.frameElement.commitPopup(retVal);\r\n                                    </script>", new object[]
									{
										serverRelativeUrl
									}));
								}
								else
								{
									this.Context.Response.Write("<script type='text/javascript'>window.frameElement.commitPopup();</script>");
								}
								this.Context.Response.Flush();
								this.Context.Response.End();
							}
						}
					}
				}
			}
			return result;
		}

		private static void ActionBeforeSaveItem(SPContext itemContext)
		{
			Type spContextType = typeof(SPContext);
			PropertyInfo spContextContentType = spContextType.GetProperty("ContentType", BindingFlags.Instance | BindingFlags.NonPublic);
			SPContentType itemContextContentType = spContextContentType.GetValue(itemContext, null) as SPContentType;

			Type spFormContextType = typeof(SPFormContext);
			PropertyInfo spFormContextNeedCompleteRecurrence = spFormContextType.GetProperty("NeedCompleteRecurrence", BindingFlags.Instance | BindingFlags.NonPublic);
			bool formContextNeedCompleteRecurrence = (bool)spFormContextNeedCompleteRecurrence.GetValue(itemContext.FormContext, null);

			Type recurrenceFieldType = typeof(RecurrenceField);
			MethodInfo recurrenceFieldCompleteItemLoad = recurrenceFieldType.GetMethod("CompleteItemLoad", BindingFlags.Instance | BindingFlags.NonPublic);

			Type allDayEventFieldType = typeof(AllDayEventField);
			MethodInfo allDayEventFieldCompleteItemLoad = allDayEventFieldType.GetMethod("CompleteItemLoad", BindingFlags.Instance | BindingFlags.NonPublic);

			if ((itemContext.List.BaseTemplate == SPListTemplateType.Events || (itemContextContentType != null && itemContextContentType.Id.IsChildOf(SPBuiltInContentTypeId.Event))) && itemContext.FormContext.FormMode != SPControlMode.Display && formContextNeedCompleteRecurrence)
			{
				ArrayList fieldControlCollection = itemContext.FormContext.FieldControlCollection;
				int num = 0;
				foreach (Control control in fieldControlCollection)
				{
					if (num > 1)
					{
						break;
					}
					if (control is RecurrenceField || control is AllDayEventField)
					{
						num++;
						if (control is RecurrenceField)
						{
							RecurrenceField recurrenceField = (RecurrenceField)control;
							recurrenceFieldCompleteItemLoad.Invoke(recurrenceField, null);
						}
						if (control is AllDayEventField)
						{
							AllDayEventField allDayEventField = (AllDayEventField)control;
							allDayEventFieldCompleteItemLoad.Invoke(allDayEventField, null);
						}
					}
				}
			}
		}

		private bool NeedToCreateWorkSpace()
		{
			Type spContextType = typeof(SPContext);
			PropertyInfo spContextContentType = spContextType.GetProperty("ContentType", BindingFlags.Instance | BindingFlags.NonPublic);
			SPContentType itemContextContentType = spContextContentType.GetValue(base.ItemContext, null) as SPContentType;

			Type spFormContextType = typeof(SPFormContext);
			PropertyInfo spFormContextWantRedirectForMWS = spFormContextType.GetProperty("WantRedirectForMWS", BindingFlags.Instance | BindingFlags.NonPublic);
			bool formContextWantRedirectForMWS = (bool)spFormContextWantRedirectForMWS.GetValue(base.ItemContext.FormContext, null);

			return (base.ItemContext.List.BaseTemplate == SPListTemplateType.Events || (itemContextContentType != null && itemContextContentType.Id.IsChildOf(SPBuiltInContentTypeId.Event))) && formContextWantRedirectForMWS;
		}

		private void GotoMeetingWorkspaceCreationPage(string strRedirectUrl)
		{
			Type spFormContextType = typeof(SPFormContext);
			PropertyInfo spFormContextNeedIDForNewMWS = spFormContextType.GetProperty("NeedIDForNewMWS", BindingFlags.Instance | BindingFlags.NonPublic);
			bool formContextNeedIDForNewMWS = (bool)spFormContextNeedIDForNewMWS.GetValue(base.ItemContext.FormContext, null);

			if (base.ItemContext.FormContext.FormMode == SPControlMode.New && formContextNeedIDForNewMWS)
			{
				strRedirectUrl = strRedirectUrl + "&Item=" + base.ItemContext.ListItem.ID.ToString(CultureInfo.InvariantCulture);
			}
			if (base.ItemContext.IsPopUI)
			{
				this.Context.Response.Write(string.Format(CultureInfo.InvariantCulture, "<script type='text/javascript'>if (window.parent) window.parent.location.href = '{0}';else window.location.href = '{0}';</script>", new object[]
				{
					SPHttpUtility.EcmaScriptStringLiteralEncode(strRedirectUrl)
				}));
				this.Context.Response.Flush();
				this.Context.Response.End();
				return;
			}
			SPUtility.Redirect(strRedirectUrl, SPRedirectFlags.Default, this.Context);
		}
	}
}
